﻿using Knlght.CH.Domain.IRepository;
using Knlght.Domain;
using Knlght.Repository.DBContext;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using EntityFramework.Extensions;
using EntityFramework.Caching;

namespace Knlght.Repository
{
    /// <summary>
    /// 仓储实现
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    public class RepositoryBase<TModel> : IRepositoryBase<TModel> where TModel : class, new()
    {
        private const int CacheTime = 600;
        public bool Add(TModel model)
        {
            using (var dbcontext = new EFDbContext())
            {
                dbcontext.Entry<TModel>(model).State = EntityState.Added;
                return dbcontext.SaveChanges() == 1;
            }
        }
        public bool AddToList(List<TModel> models)
        {
            using (var dbcontext = new EFDbContext())
            {
                //虽然在事务using语句中，但 dbcontext.BulkInsert(entityArray); 不会回滚事务，加上参数，才能回滚：dbcontext.BulkInsert(entityArray, tran.UnderlyingTransaction);
                // var tran = dbcontext.Database.BeginTransaction();
                // dbcontext.BulkInsert(models, tran.UnderlyingTransaction);
                dbcontext.BulkInsert(models);
                dbcontext.BulkSaveChanges();
                return true;
            }
        }
        public bool Update(TModel model)
        {
            using (var dbcontext = new EFDbContext())
            {
                if (dbcontext.Entry<TModel>(model).State == EntityState.Modified)
                {
                    return dbcontext.SaveChanges() == 1;
                }
                else if (dbcontext.Entry<TModel>(model).State == EntityState.Detached)
                {
                    try
                    {
                        dbcontext.Set<TModel>().Attach(model);
                        dbcontext.Entry<TModel>(model).State = EntityState.Modified;
                    }
                    catch (InvalidOperationException e)
                    {
                        throw new EJApplicationException(e.Message);
                    }
                    return dbcontext.SaveChanges() == 1;
                }
                return false;
            }
        }
        #region 使用EF扩展EntityFramework.Extended 对数据进行批量更新，和批量删除
        public bool UpdateToList(QueryRequest<TModel> request, Expression<Func<TModel, TModel>> key)
        {
            using (var dbcontext = new EFDbContext())
            {
                //调用插件的Update方法进行批量更新（不需要我们手动的db.SaveChanges()了）  
                //dbcontext.Set<TModel>().Update(key);//批量将location表里version字段数据更新为123  
                var list = dbcontext.Set<TModel>().Where(request.SubQueries).AsNoTracking();
                return list.Update(key) > 0;
            }
        }
        public bool Delete(QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
                //调用插件的Delete方法进行批量删除（不需要我们手动的db.SaveChanges()了）  
                //dbcontext.Set<TModel>().Delete(predicate);        
                return dbcontext.Set<TModel>().Where(request.SubQueries).AsNoTracking().Delete() > 0;
            }
        }
        #endregion
        public bool IsExist(QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Set<TModel>().AsNoTracking().Any(request.SubQueries);
            }
        }
        public TModel GetModel(object keyValue)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Set<TModel>().Find(keyValue);
            }
        }

        public TModel GetModel(Expression<Func<TModel, bool>> predicate)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Set<TModel>().FirstOrDefault(predicate);
            }
        }

        public List<TModel> GetList()
        {
            using (var dbcontext = new EFDbContext())
            {            
                return dbcontext.Set<TModel>().AsNoTracking().ToList();
            }
        }

        public List<TModel> GetList(Expression<Func<TModel, bool>> predicate )
        {
            using (var dbcontext = new EFDbContext())
            {
               return dbcontext.Set<TModel>().Where(predicate).AsNoTracking().ToList();              
            }
        }

        public TModel GetModel(QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Set<TModel>().AsNoTracking().FirstOrDefault(request.SubQueries);
            }
        }

        public List<TModel> GetList(QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
               return dbcontext.Set<TModel>().Where(request.SubQueries).AsNoTracking().ToList();
            }
        }
        public List<TModel> GetListOnCache(QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Set<TModel>().Where(request.SubQueries).AsNoTracking().FromCache(CachePolicy.WithDurationExpiration(TimeSpan.FromSeconds(CacheTime))).ToList();
            }
        }
        public List<TModel> GetList(string strSql)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Database.SqlQuery<TModel>(strSql).ToList();
            }
        }
        public List<TModel> GetList(string strSql, DbParameter[] dbParameter)
        {
            using (var dbcontext = new EFDbContext())
            {
                return dbcontext.Database.SqlQuery<TModel>(strSql, dbParameter).ToList();
            }
        }
        public List<TModel> GetList(int pageSize, int pageIndex, out int total, QueryRequest<TModel> request)
        {
            using (var dbcontext = new EFDbContext())
            {
                if (request.OrderBys == null || request.OrderBys.Count == 0)
                {
                    request.AddOrder(typeof(TModel).GetProperties()[0].Name, false);
                }
                var list = dbcontext.Set<TModel>().Where(request.SubQueries);
                //创建表达式变量参数
                var parameter = Expression.Parameter(typeof(TModel), "o");
                for (int i = 0; i < request.OrderBys.Count; i++)
                {
                    var item = request.OrderBys.ElementAt(i);
                    //根据属性名获取属性
                    var property = typeof(TModel).GetProperty(item.Key);
                    //创建一个访问属性的表达式
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
                    string OrderName = "";
                    if (i > 0)
                    {
                        OrderName = item.Value ? "ThenByDescending" : "ThenBy";
                    }
                    else
                        OrderName = item.Value ? "OrderByDescending" : "OrderBy";
                    MethodCallExpression resultExp =
                    Expression.Call(typeof(Queryable),
                    OrderName, new Type[] { typeof(TModel),
                            property.PropertyType },
                    list.Expression, Expression.Quote(orderByExp));
                    list = list.Provider.CreateQuery<TModel>(resultExp);
                }

                total = list.Count();
                return list.Skip(pageSize * (pageIndex - 1)).Take(pageSize).AsNoTracking().ToList();
            }
        }

        /// <summary>
        /// 直接获取特定一个或者多个字段的值
        /// 多个字段需要声明Model
        /// </summary>
        /// <typeparam name="T"></typeparam>       
        /// <param name="request"></param>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public T GetScalar<T>(QueryRequest<TModel> request, Expression<Func<TModel, T>> scalar)
    {
        using (var dbcontext = new EFDbContext())
        {
            return request == null ? dbcontext.Set<TModel>().AsNoTracking().Select(scalar).FirstOrDefault() : dbcontext.Set<TModel>().AsNoTracking().Where(request.SubQueries).Select(scalar).FirstOrDefault();
        }
    }
    public List<TModel> GetListScalar(QueryRequest<TModel> request, Expression<Func<TModel, TModel>> scalar)
    {
        using (var dbcontext = new EFDbContext())
        {
            if (request == null)
            {
                var list = dbcontext.Set<TModel>().AsNoTracking().ToList();
                return list.AsQueryable().AsNoTracking().Select(scalar).ToList();
            }
            else
            {
                var list = dbcontext.Set<TModel>().Where(request.SubQueries).AsNoTracking().ToList();
                return list.AsQueryable().AsNoTracking().Select(scalar).ToList();
            }
        }
    }
}
}
